{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "64b57da1-db0c-4bd9-b05d-fbdf9deca720",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建FLANN匹配器 FlannBasedMatcher(...)\n",
    "# 进行特征匹配 flann.match/knnMatch(...)\n",
    "# 绘制匹配点 cv2.drawMatches/drawMatcherKnn(...)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "875a4d2c-db0b-4a48-8066-532497fa6748",
   "metadata": {},
   "outputs": [],
   "source": [
    "# index_params字典：匹配算法KDTRESS,LSH\n",
    "# search_param字典：指定KDTREE算法中遍历树的次数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e25fb383-c4bc-4792-8b56-adc0f425b482",
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a2cbe2ef-e2e7-42e3-98bb-91b3da973505",
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "import ctypes\n",
    "\n",
    "def drawPicture(name, pic):\n",
    "    # 定义用于获取窗口大小的Windows API函数\n",
    "    user32 = ctypes.windll.user32\n",
    "    \n",
    "    # 设置可调整大小的窗口\n",
    "    cv2.namedWindow(name, cv2.WINDOW_NORMAL)\n",
    "    \n",
    "    while True:\n",
    "        # 设置窗口的宽度和高度\n",
    "        window_width = 1920\n",
    "        window_height = 1080\n",
    "    \n",
    "        # 根据窗口大小调整图片尺寸\n",
    "        resized_image = cv2.resize(pic, (window_width, window_height))\n",
    "    \n",
    "        # 重新显示调整后的图片\n",
    "        cv2.imshow(name, resized_image)\n",
    "    \n",
    "        if cv2.waitKey(0):\n",
    "            break\n",
    "    \n",
    "    cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "74d0933d-59af-4073-a568-3489bb919034",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "378\n",
      "504\n"
     ]
    }
   ],
   "source": [
    "# 读取两张图片\n",
    "img1 = cv2.imread('targ1.png')\n",
    "img2 = cv2.imread('targ2.png')\n",
    "\n",
    "# 指定缩放比例\n",
    "scale_factor = 0.125\n",
    "\n",
    "# 根据缩放比例计算新的宽度和高度值\n",
    "new_width = int(img1.shape[1] * scale_factor)\n",
    "new_height = int(img1.shape[0] * scale_factor)\n",
    "\n",
    "print(new_width)\n",
    "print(new_height)\n",
    "\n",
    "# 进行尺寸调整\n",
    "img1 = cv2.resize(img1, (new_width, new_height))\n",
    "img2 = cv2.resize(img2, (new_width, new_height))\n",
    "\n",
    "# 拼接图片左右显示\n",
    "combined_image = np.hstack((img1, img2))\n",
    "\n",
    "# 显示图像窗口\n",
    "cv2.imshow('original', combined_image)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ed74b8d8-a7a1-48fa-adae-342d8f31fc2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 灰度化\n",
    "gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)\n",
    "gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "# 拼接图片左右显示\n",
    "combined_image = np.hstack((gray1, gray2))\n",
    "\n",
    "# 显示图像窗口\n",
    "cv2.imshow('gray', combined_image)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "1718b6f5-8192-4c27-8314-002f268fbb44",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建SIFT特征检测器\n",
    "sift = cv2.xfeatures2d.SIFT_create()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "7a2253dd-fa18-430a-bf7e-83ed72fc44d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "#计算描述子与特征点\n",
    "kp1, des1 = sift.detectAndCompute(gray1, None)\n",
    "kp2, des2 = sift.detectAndCompute(gray2, None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "9f8b2155-dbd6-4592-8d78-04263c5025ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建匹配器\n",
    "index_params = dict(algorithm = 1, trees = 5)\n",
    "search_params = dict(checks = 50)\n",
    "flann = cv2.FlannBasedMatcher(index_params, search_params)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "7d025a7b-2668-4efe-ba0b-9bd89b304736",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "get 18 match description\n"
     ]
    }
   ],
   "source": [
    "# 对描述子进行匹配\n",
    "matchs = flann.knnMatch(des1, des2, k=2)\n",
    "\n",
    "good = []\n",
    "for i, (m,n) in enumerate(matchs):\n",
    "    if m.distance < 0.4*n.distance:\n",
    "        good.append(m)\n",
    "\n",
    "print('get {} match description'.format(len(good)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "faec4f7d-dcd7-40cb-8f28-35c347043bd6",
   "metadata": {},
   "outputs": [],
   "source": [
    "ret = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)\n",
    "\n",
    "# 绘制匹配的特征点\n",
    "drawPicture('result', ret)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "52ff3a61-f32a-499e-bb75-afecbab2b72c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Homography maxtrix: \n",
      "[[ 1.02263447e+00  1.27186645e-01 -1.12168952e+02]\n",
      " [ 9.70559343e-03  9.36570531e-01  3.85575762e+01]\n",
      " [ 3.67462750e-04 -1.50658185e-04  1.00000000e+00]]\n",
      "src picture corner: \n",
      "[[[  0.   0.]]\n",
      "\n",
      " [[  0. 503.]]\n",
      "\n",
      " [[377. 503.]]\n",
      "\n",
      " [[377.   0.]]]\n",
      "src picture 4 corner in dst picture:\n",
      " [[[-112.16895    38.557575]]\n",
      "\n",
      " [[ -52.14573   551.44135 ]]\n",
      "\n",
      " [[ 317.42026   483.00204 ]]\n",
      "\n",
      " [[ 240.10207    37.079792]]]\n"
     ]
    }
   ],
   "source": [
    "# 第一部分：图像查找\n",
    "\n",
    "if len(good) >= 4: # 匹配的特征点数量大于等于4才能构建单应性矩阵\n",
    "    srcPts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2) # img1 当中queryIdx等待用于批判特征点的idx\n",
    "    dstPts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2) # img1 当中queryIdx对应的特征点，在img2 当中对应trainIdx\n",
    "\n",
    "    # print('src picture description points {}'.format(srcPts))\n",
    "    # print('dst picture description points {}'.format(dstPts))\n",
    "    \n",
    "    H,_ = cv2.findHomography(srcPts, dstPts, cv2.RANSAC, 5.0) # 计算两幅图之间的单应性矩阵\n",
    "    print('Homography maxtrix: \\n{}'.format(H))\n",
    "    \n",
    "    h, w = img1.shape[:2]\n",
    "    pts = np.float32([[0,0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1,2) # 得到源图片img1的四个角坐标\n",
    "    dst = cv2.perspectiveTransform(pts, H) # 将源图片img1的四个角坐标经过单应性矩阵，变换到目标图片img2当中\n",
    "\n",
    "    print('src picture corner: \\n{}'.format(pts))\n",
    "    print('src picture 4 corner in dst picture:\\n {}'.format(dst))\n",
    "\n",
    "    cv2.polylines(img2, [np.int32(dst)], isClosed=True, color=(0, 0, 255), thickness=2)\n",
    "else:\n",
    "    pinrt('the number of good is less than 4')\n",
    "    exit()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "ad0a8eae-0443-43b9-b271-e294a8408680",
   "metadata": {},
   "outputs": [],
   "source": [
    "ret = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)\n",
    "drawPicture('result', ret) # 绘制src图片，经过单应性矩阵匹配到dst图片当中，并画出src在dst当中的位置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "b97d4435-5d35-4649-a3e8-21b9fe69e358",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 第二部分：图像拼接\n",
    "# 1、读取图片并重置尺寸\n",
    "# 2、根据特征点和计算描述子，得到单应性矩阵\n",
    "# 3、图像变换\n",
    "# 4、图像拼接并输出图像\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c9275d0a-c3b5-4805-9366-e11c07d87f40",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
